//
//  CompressionManager.swift
//  ZipTool
//
//  Created by jerry on 2025/9/14.
//

import Foundation
import Compression

struct CompressionItem {
    let url: URL
    let relativePath: String
    let isDirectory: Bool
    let size: Int64
    
    init(url: URL, basePath: URL? = nil) {
        self.url = url
        self.isDirectory = url.hasDirectoryPath
        
        if let basePath = basePath {
            self.relativePath = String(url.path.dropFirst(basePath.path.count + 1))
        } else {
            self.relativePath = url.lastPathComponent
        }
        
        if isDirectory {
            self.size = 0
        } else {
            do {
                let attributes = try FileManager.default.attributesOfItem(atPath: url.path)
                self.size = attributes[.size] as? Int64 ?? 0
            } catch {
                self.size = 0
            }
        }
    }
}

enum CompressionFormat: String, CaseIterable {
    case zip = "zip"
    case sevenZ = "7z"
    case tar = "tar"
    case tarGz = "tar.gz"
    
    var displayName: String {
        switch self {
        case .zip:
            return "ZIP"
        case .sevenZ:
            return "7-Zip"
        case .tar:
            return "TAR"
        case .tarGz:
            return "TAR.GZ"
        }
    }
    
    var fileExtension: String {
        return rawValue
    }
}

class CompressionManager: ObservableObject {
    @Published var items: [CompressionItem] = []
    @Published var isCompressing = false
    @Published var compressionProgress: Double = 0
    @Published var errorMessage: String?
    
    func addItems(urls: [URL]) {
        for url in urls {
            addItem(url: url)
        }
    }
    
    private func addItem(url: URL) {
        // 检查是否已经存在
        if items.contains(where: { $0.url == url }) {
            return
        }
        
        let item = CompressionItem(url: url)
        items.append(item)
        
        // 如果是目录，递归添加子项
        if item.isDirectory {
            addDirectoryContents(url: url, basePath: url.deletingLastPathComponent())
        }
    }
    
    private func addDirectoryContents(url: URL, basePath: URL) {
        do {
            let fileManager = FileManager.default
            let contents = try fileManager.contentsOfDirectory(at: url, includingPropertiesForKeys: [.isDirectoryKey], options: [])
            
            for contentURL in contents {
                let item = CompressionItem(url: contentURL, basePath: basePath)
                items.append(item)
                
                if item.isDirectory {
                    addDirectoryContents(url: contentURL, basePath: basePath)
                }
            }
        } catch {
            print("Error reading directory contents: \(error)")
        }
    }
    
    func removeItem(at index: Int) {
        guard index < items.count else { return }
        let itemToRemove = items[index]
        
        // 如果是目录，也移除其子项
        if itemToRemove.isDirectory {
            items.removeAll { item in
                item.relativePath.hasPrefix(itemToRemove.relativePath + "/") || item.url == itemToRemove.url
            }
        } else {
            items.remove(at: index)
        }
    }
    
    func clearAll() {
        items.removeAll()
    }
    
    func createArchive(at destinationURL: URL, format: CompressionFormat, progressHandler: @escaping (Double) -> Void) {
        guard !items.isEmpty else {
            errorMessage = "没有要压缩的文件"
            return
        }
        
        isCompressing = true
        compressionProgress = 0
        errorMessage = nil
        
        DispatchQueue.global(qos: .userInitiated).async {
            do {
                switch format {
                case .zip:
                    try self.createZipArchive(at: destinationURL, progressHandler: progressHandler)
                case .tar:
                    try self.createTarArchive(at: destinationURL, progressHandler: progressHandler)
                case .tarGz:
                    try self.createTarGzArchive(at: destinationURL, progressHandler: progressHandler)
                default:
                    throw CompressionError.unsupportedFormat
                }
                
                DispatchQueue.main.async {
                    self.isCompressing = false
                    progressHandler(1.0)
                }
            } catch {
                DispatchQueue.main.async {
                    self.errorMessage = "压缩失败: \(error.localizedDescription)"
                    self.isCompressing = false
                }
            }
        }
    }
    
    private func createZipArchive(at destinationURL: URL, progressHandler: @escaping (Double) -> Void) throws {
        // 创建临时工作目录
        let tempWorkDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
        try FileManager.default.createDirectory(at: tempWorkDir, withIntermediateDirectories: true)
        defer {
            try? FileManager.default.removeItem(at: tempWorkDir)
        }
        
        let coordinator = NSFileCoordinator()
        var coordinatorError: NSError?
        let totalItems = items.count
        
        // 使用 NSFileCoordinator 安全地复制文件到临时目录
        for (index, item) in items.enumerated() {
            let targetURL = tempWorkDir.appendingPathComponent(item.relativePath)
            
            if item.isDirectory {
                try FileManager.default.createDirectory(at: targetURL, withIntermediateDirectories: true)
            } else {
                let targetDir = targetURL.deletingLastPathComponent()
                try FileManager.default.createDirectory(at: targetDir, withIntermediateDirectories: true)
                
                // 获取安全访问权限
                let hasAccess = item.url.startAccessingSecurityScopedResource()
                defer {
                    if hasAccess {
                        item.url.stopAccessingSecurityScopedResource()
                    }
                }
                
                // 直接复制文件
                do {
                    let data = try Data(contentsOf: item.url)
                    try data.write(to: targetURL)
                } catch {
                    print("Error copying file: \(error)")
                    coordinatorError = error as NSError
                }
                
                if let error = coordinatorError {
                    throw error
                }
            }
            
            DispatchQueue.main.async {
                progressHandler(Double(index + 1) / Double(totalItems) * 0.8)
            }
        }
        
        // 在临时目录创建压缩包，避免权限问题
        let tempZipURL = tempWorkDir.appendingPathComponent("archive.zip")
        
        // 使用 zip 命令创建压缩包
        let process = Process()
        process.launchPath = "/usr/bin/zip"
        process.arguments = ["-r", tempZipURL.path, "."]
        process.currentDirectoryPath = tempWorkDir.path
        
        let pipe = Pipe()
        process.standardOutput = pipe
        process.standardError = pipe
        
        try process.run()
        process.waitUntilExit()
        
        if process.terminationStatus != 0 {
            let data = pipe.fileHandleForReading.readDataToEndOfFile()
            let output = String(data: data, encoding: .utf8) ?? "Unknown error"
            throw CompressionError.zipCreationFailed(output)
        }
        
        // 获取目标文件目录的写入权限
        let destinationDir = destinationURL.deletingLastPathComponent()
        let hasDestAccess = destinationDir.startAccessingSecurityScopedResource()
        defer {
            if hasDestAccess {
                destinationDir.stopAccessingSecurityScopedResource()
            }
        }
        
        // 将压缩包从临时目录移动到目标位置
        if FileManager.default.fileExists(atPath: destinationURL.path) {
            try FileManager.default.removeItem(at: destinationURL)
        }
        try FileManager.default.moveItem(at: tempZipURL, to: destinationURL)
        
        DispatchQueue.main.async {
            progressHandler(1.0)
        }
    }
    
    private func createTarArchive(at destinationURL: URL, progressHandler: @escaping (Double) -> Void) throws {
        // 创建临时工作目录
        let tempWorkDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
        try FileManager.default.createDirectory(at: tempWorkDir, withIntermediateDirectories: true)
        defer {
            try? FileManager.default.removeItem(at: tempWorkDir)
        }
        
        let coordinator = NSFileCoordinator()
        var coordinatorError: NSError?
        let totalItems = items.count
        
        // 使用 NSFileCoordinator 安全地复制文件到临时目录
        for (index, item) in items.enumerated() {
            let targetURL = tempWorkDir.appendingPathComponent(item.relativePath)
            
            if item.isDirectory {
                try FileManager.default.createDirectory(at: targetURL, withIntermediateDirectories: true)
            } else {
                let targetDir = targetURL.deletingLastPathComponent()
                try FileManager.default.createDirectory(at: targetDir, withIntermediateDirectories: true)
                
                // 获取安全访问权限
                let hasAccess = item.url.startAccessingSecurityScopedResource()
                defer {
                    if hasAccess {
                        item.url.stopAccessingSecurityScopedResource()
                    }
                }
                
                // 直接复制文件
                do {
                    let data = try Data(contentsOf: item.url)
                    try data.write(to: targetURL)
                } catch {
                    print("Error copying file: \(error)")
                    coordinatorError = error as NSError
                }
                
                if let error = coordinatorError {
                    throw error
                }
            }
            
            DispatchQueue.main.async {
                progressHandler(Double(index + 1) / Double(totalItems) * 0.8)
            }
        }
        
        // 在临时目录创建归档，避免权限问题
        let tempTarURL = tempWorkDir.appendingPathComponent("archive.tar")
        
        // 使用 tar 命令创建归档
        let process = Process()
        process.launchPath = "/usr/bin/tar"
        process.arguments = ["-cf", tempTarURL.path, "."]
        process.currentDirectoryPath = tempWorkDir.path
        
        let pipe = Pipe()
        process.standardOutput = pipe
        process.standardError = pipe
        
        try process.run()
        process.waitUntilExit()
        
        if process.terminationStatus != 0 {
            let data = pipe.fileHandleForReading.readDataToEndOfFile()
            let output = String(data: data, encoding: .utf8) ?? "Unknown error"
            throw CompressionError.tarCreationFailed(output)
        }
        
        // 获取目标文件目录的写入权限
        let destinationDir = destinationURL.deletingLastPathComponent()
        let hasDestAccess = destinationDir.startAccessingSecurityScopedResource()
        defer {
            if hasDestAccess {
                destinationDir.stopAccessingSecurityScopedResource()
            }
        }
        
        // 将归档从临时目录移动到目标位置
        if FileManager.default.fileExists(atPath: destinationURL.path) {
            try FileManager.default.removeItem(at: destinationURL)
        }
        try FileManager.default.moveItem(at: tempTarURL, to: destinationURL)
        
        DispatchQueue.main.async {
            progressHandler(1.0)
        }
    }
    
    private func createTarGzArchive(at destinationURL: URL, progressHandler: @escaping (Double) -> Void) throws {
        // 创建临时工作目录
        let tempWorkDir = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
        try FileManager.default.createDirectory(at: tempWorkDir, withIntermediateDirectories: true)
        defer {
            try? FileManager.default.removeItem(at: tempWorkDir)
        }
        
        let coordinator = NSFileCoordinator()
        var coordinatorError: NSError?
        let totalItems = items.count
        
        // 使用 NSFileCoordinator 安全地复制文件到临时目录
        for (index, item) in items.enumerated() {
            let targetURL = tempWorkDir.appendingPathComponent(item.relativePath)
            
            if item.isDirectory {
                try FileManager.default.createDirectory(at: targetURL, withIntermediateDirectories: true)
            } else {
                let targetDir = targetURL.deletingLastPathComponent()
                try FileManager.default.createDirectory(at: targetDir, withIntermediateDirectories: true)
                
                // 获取安全访问权限
                let hasAccess = item.url.startAccessingSecurityScopedResource()
                defer {
                    if hasAccess {
                        item.url.stopAccessingSecurityScopedResource()
                    }
                }
                
                // 直接复制文件
                do {
                    let data = try Data(contentsOf: item.url)
                    try data.write(to: targetURL)
                } catch {
                    print("Error copying file: \(error)")
                    coordinatorError = error as NSError
                }
                
                if let error = coordinatorError {
                    throw error
                }
            }
            
            DispatchQueue.main.async {
                progressHandler(Double(index + 1) / Double(totalItems) * 0.8)
            }
        }
        
        // 在临时目录创建压缩归档，避免权限问题
        let tempTarGzURL = tempWorkDir.appendingPathComponent("archive.tar.gz")
        
        // 使用 tar 命令创建压缩归档
        let process = Process()
        process.launchPath = "/usr/bin/tar"
        process.arguments = ["-czf", tempTarGzURL.path, "."]
        process.currentDirectoryPath = tempWorkDir.path
        
        let pipe = Pipe()
        process.standardOutput = pipe
        process.standardError = pipe
        
        try process.run()
        process.waitUntilExit()
        
        if process.terminationStatus != 0 {
            let data = pipe.fileHandleForReading.readDataToEndOfFile()
            let output = String(data: data, encoding: .utf8) ?? "Unknown error"
            throw CompressionError.tarGzCreationFailed(output)
        }
        
        // 获取目标文件目录的写入权限
        let destinationDir = destinationURL.deletingLastPathComponent()
        let hasDestAccess = destinationDir.startAccessingSecurityScopedResource()
        defer {
            if hasDestAccess {
                destinationDir.stopAccessingSecurityScopedResource()
            }
        }
        
        // 将压缩归档从临时目录移动到目标位置
        if FileManager.default.fileExists(atPath: destinationURL.path) {
            try FileManager.default.removeItem(at: destinationURL)
        }
        try FileManager.default.moveItem(at: tempTarGzURL, to: destinationURL)
        
        DispatchQueue.main.async {
            progressHandler(1.0)
        }
    }
    
    var totalSize: Int64 {
        return items.reduce(0) { $0 + $1.size }
    }
    
    var fileCount: Int {
        return items.filter { !$0.isDirectory }.count
    }
    
    var directoryCount: Int {
        return items.filter { $0.isDirectory }.count
    }
}

enum CompressionError: LocalizedError {
    case unsupportedFormat
    case fileNotFound
    case permissionDenied
    case zipCreationFailed(String)
    case tarCreationFailed(String)
    case tarGzCreationFailed(String)
    
    var errorDescription: String? {
        switch self {
        case .unsupportedFormat:
            return "不支持的压缩格式"
        case .fileNotFound:
            return "文件未找到"
        case .permissionDenied:
            return "权限不足"
        case .zipCreationFailed(let message):
            return "ZIP 创建失败: \(message)"
        case .tarCreationFailed(let message):
            return "TAR 创建失败: \(message)"
        case .tarGzCreationFailed(let message):
            return "TAR.GZ 创建失败: \(message)"
        }
    }
}